package algebra.visitors.cursors;

import kb.DescriptionLogicsKB;
import option.KBVocabulary;
import reasoner.KBReason;
import utils.exception.CoreReasoningException;
import algebra.query.indexing.DescriptionIndex;
import algebra.query.indexing.IndexTreeNode;
import algebra.query.od.OrderDep;
import algebra.query.od.OrderDepOid;
import algebra.visitors.AlgebraicOPEvaluator;
import entities.assertion.ConceptAssertion;
import entities.concept.Concept;

public class CACursorDI extends CACursor {
	protected final DescriptionIndex di;
	protected ConceptAssertion ca;
	protected boolean star;
	protected final IndexTreeNode root;
	//note that the search order is implicit: the oder defined in this index
	//results order is different from the above one: it specifies whether results are sorted by Ind or Un.
	protected boolean resOd;
	protected final Concept scanQueryDefCp;
	private CursorType type;
	private boolean notest = false;
	public static enum CursorType{ TRA, SORT};
	//use stat
	private final static double minSelectivity = 0.3; // 30%
	private final static int maxTop = 0; //num of tops that can occur in this index.
	//placeholders for cursors
	private CACursorDITraversal trav;
	private CACursorDIOrderById sort;
	
	public CACursorDI(DescriptionIndex di, ConceptAssertion ca, IndexTreeNode root, boolean resultOd, Concept d){
		this.di = di;
		this.root = root;
		this.scanQueryDefCp = d;
		if(ca != null){
			this.ca = new ConceptAssertion(ca.getIndividual(), ca.getConcept());
		}else{
			this.ca= null;
		}
		try {
			this.preprocessSearchCondition();
		} catch (CoreReasoningException e) {
			e.printStackTrace();
		}
		if (ca != null) {
			star = AlgebraicOPEvaluator.isSpecialAssertion(ca);
		} else {
			star = true;
		}
		this.resOd = resultOd;
	}
	
	private void preprocessSearchCondition() throws CoreReasoningException{
		 //if the supplied condition can be simplified, i.e., unsat? top? or their equivalents w.r.t. empty KB
		if(this.ca == null){
			return;
		}
		Concept cp = this.ca.getConcept();
		KBReason res = new KBReason();
		DescriptionLogicsKB kb = null;
		if(res.subsumes(cp, KBVocabulary.CONCEPT_BOTTOM, kb)){
			this.ca = new ConceptAssertion(this.ca.getIndividual(), KBVocabulary.CONCEPT_BOTTOM);
		}else
		if(res.subsumes(KBVocabulary.CONCEPT_TOP, cp, kb)){
			this.ca = new ConceptAssertion(this.ca.getIndividual(), KBVocabulary.CONCEPT_TOP);
		}
	}
	
	@Override
	public boolean openCursor() {
		if(this.ca == null){
			return false;
		}
		this.chooseCursor();
//		System.out.println("type: "+this.type);
		switch(this.type){
		case TRA: 
			this.trav = new CACursorDITraversal(this.di, this.ca, this.scanQueryDefCp, this.notest);
			this.trav.openCursor();
			break;
		case SORT:
			this.sort = new CACursorDIOrderById(this.di, this.ca, this.root, this.scanQueryDefCp);
			this.sort.openCursor();
			break;
		default: return false;
		}
		return true;
	}

	@Override
	public ConceptAssertion getNextResult() {
		if(this.ca == null){
			return null;
		}
		switch(this.type){
		case TRA: 
			return this.trav.getNextResult();
		case SORT:
			return this.sort.getNextResult();
		default: return null;
		}
	}
	
	public static boolean reasoningRequired(boolean star, Concept qc, Concept sc){
		boolean reason = true;
		if(star){
			// optimization: if the Scan_si_(D) and {}|=D' < D where si=(D', pd),
			// then return *all* entries in si immediately!
			DescriptionLogicsKB kb = null;
			try {
				reason = ! new KBReason().
						subsumesGuarded(sc, qc, kb);
			} catch (CoreReasoningException e1) {
				e1.printStackTrace();
			}
			// end optimization
		}
		return reason;
	}
	
	public static CursorType cursorType(boolean star, Concept sc, DescriptionIndex di){
		boolean compatiable = conceptForSearch(star, sc, di);
		if(!compatiable){
			return CursorType.TRA;
		}else
		if(di.getNumTops() > maxTop){
			return CursorType.TRA;
		}else{
			double perc = di.getNumOfFeatureValues() * 1.0 / di.getSize();
			if(perc < minSelectivity){
				return CursorType.TRA;
			}else{
				return CursorType.SORT;
			}
		}
	}
	
	private void chooseCursor(){
		//default
		boolean test = reasoningRequired(this.star, this.ca.getConcept(), this.scanQueryDefCp);	
		if(!test){
			this.type = CursorType.TRA;
			this.notest = true;
		}else{
			this.type = cursorType(this.star, this.ca.getConcept(), this.di);
		}
	}

	// test if the given search query is suitable for search, i.e., compatible with od
	private static boolean conceptForSearch(boolean star, Concept sc, DescriptionIndex di) {
		if (!star) {
			// use search
			return true;
		}
		OrderDep od = di.getOd();
		if (od.majorSortOnArg(sc)) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isSortedById() {
		if(this instanceof CACursorDITraversal){
			return this.di.getOd() instanceof OrderDepOid;
		}else if(this instanceof CACursorDIOrderById){
			return this.resOd;
		}
		return false;
	}


}
